const fs = require('fs');
const path = require('path');
const configCreator = require('../lib/frontend-config-creator');

jest.mock('@aws-amplify/amplify-cli-core');

const mapServiceName = 'Map';
const placeIndexServiceName = 'PlaceIndex';
const geofenceCollectionServiceName = 'GeofenceCollection';

describe('Get current aws-exports', () => {
  let context;
  const awsmobile = {
    aws_project_region: 'us-east-1',
  };

  const generateAwsExportsFileContents = (awsmobile) => `
/* eslint-disable */
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = ${JSON.stringify(awsmobile, null, 2)};

module.exports = awsmobile;
`;
  const awsExports = generateAwsExportsFileContents(awsmobile);
  const projectPath = path.resolve('./');
  const srcDirPath = 'lib'; // change if need be
  const awsExportsPath = path.join(projectPath, srcDirPath, 'aws-exports.js');
  beforeAll(() => {
    context = {
      amplify: {
        getEnvInfo: jest.fn().mockReturnValue({
          projectPath,
        }),
        getProjectConfig: jest.fn().mockReturnValue({
          javascript: {
            config: {
              SourceDir: srcDirPath,
            },
          },
        }),
      },
    };
  });

  beforeEach(() => {
    fs.writeFileSync(awsExportsPath, awsExports);
  });

  afterEach(() => {
    fs.unlinkSync(awsExportsPath);
  });

  it('should load ESM', async () => {
    const result = await configCreator.getCurrentAWSExports(context);
    expect(result).toEqual(awsmobile);
  });

  it('should load CommonJS', async () => {
    const result = await configCreator.getCurrentAWSExports(context);
    expect(result).toEqual(awsmobile);
    // expect(configCreator.getCurrentAWSExports(context)).toThrowError('Unable to parse aws-exports.js. Has this file been modified?');
  });

  it('should load with babel.config.json present', async () => {
    const babelConfigPath = path.join(projectPath, 'babel.config.json');
    const babelConfigContent = JSON.stringify({ presets: ['es2015'] });
    try {
      fs.writeFileSync(babelConfigPath, babelConfigContent);
      const result = await configCreator.getCurrentAWSExports(context);
      expect(result).toEqual(awsmobile);
    } finally {
      fs.unlinkSync(babelConfigPath);
    }
  });

  it('should throw error if file does not have exports', async () => {
    expect.assertions(1);
    fs.writeFileSync(awsExportsPath, generateAwsExportsFileContents(null));
    const ERROR_MESSAGE = 'Unable to find aws-exports.js. Has this file been modified?';
    await expect(configCreator.getCurrentAWSExports(context)).rejects.toThrow(ERROR_MESSAGE);
  });

  it('should throw error if file contains a syntax error', async () => {
    expect.assertions(1);
    fs.writeFileSync(awsExportsPath, 'awsmobile = {');
    const ERROR_MESSAGE = 'Unable to parse aws-exports.js. Has this file been modified?';
    await expect(configCreator.getCurrentAWSExports(context)).rejects.toThrow(ERROR_MESSAGE);
  });
});

describe('generate maps and search configuration', () => {
  function constructMapMeta(mapName, mapStyle, isDefault, region) {
    return {
      service: mapServiceName,
      output: {
        Style: mapStyle,
        Name: mapName,
        Region: region,
      },
      isDefault,
    };
  }

  function constructPlaceIndexMeta(indexName, isDefault, region) {
    return {
      service: placeIndexServiceName,
      output: {
        Name: indexName,
        Region: region,
      },
      isDefault,
    };
  }

  function constructGeofenceCollectionMeta(collectionName, isDefault, region) {
    return {
      service: geofenceCollectionServiceName,
      output: {
        Name: collectionName,
        Region: region,
      },
      isDefault,
    };
  }

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('generates correct configuration for maps and search geo resources without Region CFN output', () => {
    const projectRegion = 'us-west-2';
    const mockGeoResources = {
      serviceResourceMapping: {
        Map: [constructMapMeta('map12345', 'VectorEsriStreets', false), constructMapMeta('defaultMap12345', 'VectorEsriStreets', true)],
        PlaceIndex: [constructPlaceIndexMeta('index12345', false), constructPlaceIndexMeta('defaultIndex12345', true)],
        GeofenceCollection: [
          constructGeofenceCollectionMeta('collection12345', false),
          constructGeofenceCollectionMeta('defaultCollection12345', true),
        ],
      },
      metadata: {
        Region: projectRegion,
      },
    };
    const generatedConfig = configCreator.getAWSExportsObject(mockGeoResources);
    expect(generatedConfig.geo.amazon_location_service.region).toEqual(projectRegion);
    expect(generatedConfig).toMatchSnapshot();
  });

  it('does not add any geo configuration if no maps or search is added', () => {
    const mockGeoResources = {
      serviceResourceMapping: {},
      metadata: {
        Region: 'us-west-2',
      },
    };
    const generatedConfig = configCreator.getAWSExportsObject(mockGeoResources);
    expect(generatedConfig.geo).toBeUndefined();
  });

  it('generates correct configuration for maps and search geo resources with Region as CFN output', () => {
    const resourceRegion = 'eu-west-1';
    const projectRegion = 'eu-west-2';
    const mockGeoResources = {
      serviceResourceMapping: {
        Map: [
          constructMapMeta('map12345', 'VectorEsriStreets', false, resourceRegion),
          constructMapMeta('defaultMap12345', 'VectorEsriStreets', true, resourceRegion),
        ],
        PlaceIndex: [
          constructPlaceIndexMeta('index12345', false, resourceRegion),
          constructPlaceIndexMeta('defaultIndex12345', true, resourceRegion),
        ],
        GeofenceCollection: [
          constructGeofenceCollectionMeta('collection12345', false, resourceRegion),
          constructGeofenceCollectionMeta('defaultCollection12345', true, resourceRegion),
        ],
      },
      metadata: {
        Region: projectRegion,
      },
    };
    const generatedConfig = configCreator.getAWSExportsObject(mockGeoResources);
    expect(generatedConfig.geo.amazon_location_service.region).toEqual(resourceRegion);
    expect(generatedConfig).toMatchSnapshot();
  });
});

describe('generates pinpoint configuration', () => {
  it('generates correct configuration for analytics and notifications channels', () => {
    const mockPinpointResources = {
      metadata: {
        Region: 'us-east-1',
      },
      serviceResourceMapping: {
        Pinpoint: [
          {
            output: {
              Region: 'us-east-1',
              Id: 'fake',
            },
          },
          {
            Id: 'fake',
            Region: 'us-east-1',
            output: {
              Region: 'us-east-1',
              InAppMessaging: {
                Enabled: true,
                ApplicationId: 'fake',
              },
              SMS: {
                ApplicationId: 'fake',
                Enabled: true,
              },
            },
          },
        ],
      },
    };
    const expectedGeneratedConfig = {
      aws_project_region: 'us-east-1',
      aws_mobile_analytics_app_id: 'fake',
      aws_mobile_analytics_app_region: 'us-east-1',
      Analytics: {
        AWSPinpoint: {
          appId: 'fake',
          region: 'us-east-1',
        },
      },
      Notifications: {
        InAppMessaging: {
          AWSPinpoint: {
            appId: 'fake',
            region: 'us-east-1',
          },
        },
        SMS: {
          AWSPinpoint: {
            appId: 'fake',
            region: 'us-east-1',
          },
        },
      },
    };
    const generatedConfig = configCreator.getAWSExportsObject(mockPinpointResources);
    expect(generatedConfig).toMatchObject(expectedGeneratedConfig);
  });
});
